import itertools

from cv2 import BORDER_CONSTANT as _BORDER_CONSTANT
from cv2 import CHAIN_APPROX_NONE as _CHAIN_APPROX_NONE
from cv2 import COLOR_BGR2RGB as _COLOR_BGR2RGB
from cv2 import COLOR_RGB2BGR as _COLOR_RGB2BGR
from cv2 import COLOR_RGB2GRAY as _COLOR_RGB2GRAY
from cv2 import RETR_EXTERNAL as _RETR_EXTERNAL
from cv2 import THRESH_BINARY as _THRESH_BINARY
from cv2 import THRESH_BINARY_INV as _THRESH_BINARY_INV
from cv2 import boundingRect as _boundingRect
from cv2 import copyMakeBorder as _copyMakeBorder
from cv2 import cvtColor as _cvtColor
from cv2 import findContours as _findContours
from cv2 import imread as _imread
from cv2 import imshow as _imshow
from cv2 import imwrite as _imwrite
from cv2 import threshold as _threshold
from cv2 import waitKey as _waitKey
from matplotlib import pyplot as _pyplot
from numpy import asarray as _asarray
from numpy import concatenate as _concatenate
from numpy import minimum as _minimum
from numpy import ndarray as _ndarray
from PIL.Image import Image as _Image
from PIL.Image import fromarray as _fromarray
from PIL.Image import new as _new
from PIL.ImageDraw import Draw as _Draw

from .comput.codings import str2utf_8 as _str2utf_8
from .comput.codings import utf_82str as _utf_82str
from .ess import dountil as _dountil
from .math.mathematic import upint as _upint


def show_plt(pic: _ndarray, title: str = '', axis: str = "off") -> None:
    """展示图片，用matplotlib.pyplot"""
    _pyplot.imshow(pic, interpolation="bilinear")
    _pyplot.title(title)
    _pyplot.axis(axis)
    _pyplot.show()


def show_cv(pic: _ndarray, title: str = '') -> None:
    """展示图片，用opencv2"""
    _imshow(title, pic)
    _waitKey(0)


def show_PIL(pic: _Image, title: str = '') -> None:
    """展示图片，用pillow"""
    pic.show(title=title)


def imread_cv(fp: str) -> _ndarray:
    """读入RGB图片，用opencv2"""
    return _cvtColor(_imread(fp), _COLOR_BGR2RGB)


def imsave_cv(img: _ndarray, fp: str) -> None:
    """保存RGB图片，用opencv2"""
    _imwrite(fp, _cvtColor(img, _COLOR_RGB2BGR))


def RGB2Gray(pic: _ndarray) -> _ndarray:
    """RGB图片转灰白，用opencv2"""
    return _cvtColor(pic, _COLOR_RGB2GRAY)


def np2pil(arr: _ndarray) -> _Image:
    """numpy转换为pillow"""
    return _fromarray(arr)


def pil2np(img: _Image) -> _ndarray:
    """pillow转换为numpy"""
    return _asarray(img)


class Border2d:
    """边界类"""

    def __init__(self, x, y, hight, weight):
        self.x: int = x
        self.y: int = y
        self.hight: int = hight
        self.weight: int = weight
        self.begin: tuple = (x, y)
        self.end: tuple = (x + weight, y + hight)

    def slice(self):
        """用于切片"""
        return slice(self.y, self.y + self.hight), slice(self.x, self.x + self.weight)

    def corner(self, horizon: str, vertical: str):
        """四个角落的坐标\n
        left|right&up|down"""
        if horizon.lower() == "left":
            if vertical.lower() == "up":
                return self.x, self.y
            elif vertical.lower() == "down":
                return self.x + self.hight, self.y
        elif horizon.lower() == "right":
            if vertical.lower() == "up":
                return self.x, self.y + self.weight
            elif vertical.lower() == "down":
                return self.x + self.hight, self.y + self.weight
        return

    def shape(self):
        """长宽"""
        return self.hight, self.weight

    def diagonal(self):
        """对角线"""
        return self.begin, self.end


def QRcode(w: str, fp: str) -> None:
    """制作二维码"""
    try:
        from MyQR import myqr as _myqr

        _myqr.run(
            words=w,
            version=20,  # 边长
            level='H',  # 超强纠错
            picture=fp,
            colorized=True,
            save_dir=".\\",
        )
    except:
        print("need install MyQR.")


def findBorderContours(pic: _ndarray):
    """寻找到图中物体最外边的边界"""
    _, contours, _ = _findContours(pic, _RETR_EXTERNAL, _CHAIN_APPROX_NONE)
    for contour in contours:
        x, y, w, h = _boundingRect(contour)
        yield Border2d(x, y, h, w)


def binarization(img: _ndarray, nagitive: bool = False) -> _ndarray:
    """图片二值化\n
    negitive负片"""
    ttype = _THRESH_BINARY_INV if nagitive else _THRESH_BINARY
    _, pic = _threshold(img, 127, 255, type=ttype)
    return pic


def printo(img: _Image, lst: list, f, x: float = 0.5, y: float = 0) -> None:
    draw = _Draw(img)
    ypos = 0
    for item in lst:
        W, H = f.getsize(item)
        draw.text(
            (
                img.width * x - (W >> 1),
                _dountil(lambda x: x >= 0, lambda x: [x + H], y + ypos)[0],
            ),
            item,
            font=f,
            fill=(0, 255, 127),
        )

        ypos += H + 1


def tosquare(pic: _ndarray) -> _ndarray:
    """把长方形图片填充成正方形"""
    ep = (max(pic.shape) - min(pic.shape)) >> 1
    ep2 = max(pic.shape) >> 2
    return _copyMakeBorder(pic, ep2, ep2, ep + ep2, ep + ep2, _BORDER_CONSTANT)


def txt2pic(txt: str) -> _Image:
    height = width = _upint(len(txt) ** 0.5)
    im = _new("RGB", (width, height), 0xF)
    x, y = 0, 0
    for i in txt:
        index = ord(i)
        g, b = index >> 8, index & 0xFF
        rgb = (abs(b - g), g, b)
        im.putpixel((x, y), rgb)
        x, y = (0, y + 1) if x == width - 1 else (x + 1, y)
    return im


def pic2txt(img: _Image) -> str:
    w, h = img.size
    lst = []
    for y in range(h):
        for x in range(w):
            _, g, b = img.getpixel((x, y))
            if (g or b) == 0:
                break
            lst.append(chr((g << 8) + b))
    return ''.join(lst)


def f_img_w_img(p: _Image, c: _Image, filt=127) -> _Image:
    """将p中每一个灰度大于flit的用c替换生成套娃图片"""
    imgs = (p.width * c.width, p.height * c.height)
    r = _new('L', imgs, "white")
    for w, h in itertools.product(range(p.width), range(p.height)):
        if p.getpixel((w, h)) < filt:
            r.paste(c, (w * c.width, h * c.height))
    return r


def clearLowBit(img: _Image) -> _Image:
    """将图像颜色最后一位置为零"""
    evenimag = _new(img.mode, img.size)
    evenimag.putdata(
        [
            (r >> 1 << 1, g >> 1 << 1, b >> 1 << 1, t >> 1 << 1)
            for (r, g, b, t) in list(img.getdata())
        ]
    )
    return evenimag


def hideInLowBit(img: _Image, data: str) -> _Image:
    """将数据写在图片颜色最低位中"""
    evenimg = clearLowBit(img.convert("RGBA"))
    binary = _str2utf_8(data)
    if len(binary) > len(img.getdata()) * 4:
        raise Exception(
            "Error:Can't encode more than "
            + str(len(evenimg.getdata()) * 4)
            + " bits in this image."
        )
    encodedpix = [
        (
            r + int(binary[i * 4]),
            g + int(binary[i * 4 + 1]),
            b + int(binary[i * 4 + 2]),
            t + int(binary[i * 4 + 3]),
        )
        if i * 4 < len(binary)
        else (r, g, b, t)
        for i, (r, g, b, t) in enumerate(list(evenimg.getdata()))
    ]
    encodedimg = _new(evenimg.mode, evenimg.size)
    encodedimg.putdata(encodedpix)
    return encodedimg


def showInLowBit(img: _Image) -> str:
    endbit = '0' * 16
    """从图片颜色最低位读取数据"""
    binary = ''.join(
        [
            str(r % 2) + str(g % 2) + str(b % 2) + str(t % 2)
            for (r, g, b, t) in list(img.getdata())
        ]
    )
    return _utf_82str(binary[:_upint(binary.find(endbit), 8)])


def NearestResize(bimg: _Image, simg: _Image) -> _Image:
    """将simg以邻近法插入bimg"""
    bw, bh = bimg.size
    sw, sh = simg.size
    dstimg = bimg.copy()
    stepx, stepy = bw / sw, bh / sh
    for i, j in itertools.product(range(sw), range(sh)):
        mapx, mapy = int(i * stepx + stepx * 0.5), int(j * stepy + stepy * 0.5)
        if mapx < bw and mapy < bh:
            dstimg.putpixel((mapx, mapy), simg.getpixel((i, j)))
    return dstimg


def genTank(img1: _ndarray, img2: _ndarray) -> _ndarray:
    """制作黑白的幻影坦克，img1为表图，img2为里图"""
    pix1 = img1.reshape(*img1.shape, 1).astype("uint16")
    pix2 = img2.reshape(*img1.shape, 1).astype("uint16")
    pix1 = _minimum(pix1, 254)
    alpha = 255 - (pix1 - pix2)
    gray = 255 * pix2 // alpha
    tank = _concatenate((gray, gray, gray, alpha), axis=2)
    tank = _minimum(tank, 255).astype("uint8")
    return tank


# def genTank_color(img1: ndarray, img2: ndarray) -> ndarray:
#     pix1 = img1.astype("uint16")
#     pix2 = img2.astype("uint16")
#     pix1 = minimum(pix1, 254)
#     alpha = 255-(pix1-pix2)
#     gray = 255*pix2//alpha
#     alpha = alpha.mean(axis=2)
#     alpha = alpha.reshape(*alpha.shape, 1)
#     tank = concatenate((gray, alpha), axis=2)
#     tank = minimum(tank, 255).astype("uint8")
#     return tank
